﻿using System;
using System.Collections.Generic;
using System.Windows.Input;
using System.Windows;
using System.Windows.Interop;
using System.Reflection;
using Keys = System.Windows.Forms.Keys;
namespace Eniux.Framework.Infrastructure.core
{
    /// <summary>
    /// WPF热键管理器
    /// 请在Window Loaded事件中注册热键
    /// </summary>
    public class HotKeyManager
    {
        #region window api

        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint controlKey, uint virtualKey);

        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        #endregion

        #region 变量

        // 热键消息编号
        private const int WM_HOTKEY = 0x0312;

        //存储热键索引
        static Dictionary<object, Dictionary<int,object>> KeyDic=new Dictionary<object,Dictionary<int,object>>();

        #endregion

        #region 热键管理(WPF专用)

        #region 注册热键

        /// <summary>
        /// 注册全局热键，不受是否获取焦点限制
        /// </summary>
        /// <param name="wnd">热键所在的Window</param>
        /// <param name="id">指定热键ID</param>
        /// <param name="controlKey">控制键</param>
        /// <param name="virtualKey">虚拟键</param>
        /// <param name="commandOrMethod">执行的命令或键盘事件委托（WPF的键盘事件委托或者Winform的键盘事件委托）</param>
        /// <returns></returns>
        public static bool RegisterHotKey(Window wnd, int id, ModifierKeys controlKey, Keys virtualKey, object commandOrMethod)
        {
            return DefaultRegistHotKey(wnd, controlKey, virtualKey, id, commandOrMethod);
        }

        /// <summary>
        /// 注册全局热键，不受是否获取焦点限制
        /// </summary>
        /// <param name="wnd">热键所在的Window</param>
        /// <param name="controlKey">控制键</param>
        /// <param name="virtualKey">虚拟键</param>
        /// <param name="commandOrMethod">执行的命令或键盘事件委托（WPF的键盘事件委托或者Winform的键盘事件委托）</param>
        /// <returns></returns>
        public static bool RegisterHotKey(Window wnd, ModifierKeys controlKey, Keys virtualKey, object commandOrMethod)
        {
            int keyid = BuildHotKeyId(controlKey, virtualKey);
            return DefaultRegistHotKey(wnd, controlKey, virtualKey, keyid, commandOrMethod);
        }

        /// <summary>
        /// 注册局部热键（当所在控件的根（window或者其他）具有焦点时，响应热键）
        /// </summary>
        /// <param name="type">获得焦点的元素的类型</param>
        /// <param name="controlKey">控制键</param>
        /// <param name="virtualKey">虚拟键</param>
        /// <param name="command">响应热键的命令</param>
        public static void RegisterHotKey(Type type, ModifierKeys controlKey, Key virtualKey, ICommand command)
        {
            CommandManager.RegisterClassInputBinding(type,
                    new InputBinding(command, new KeyGesture(virtualKey, controlKey)));
        }

        /// <summary>
        /// 注册局部热键（当所在控件的根（window或者其他）具有焦点时，响应热键）
        /// </summary>
        /// <param name="type">获得焦点的元素的类型</param>
        /// <param name="gesture">快捷键</param>
        /// <param name="command">响应热键的命令</param>
        public static void RegisterHotKey(Type type, KeyGesture gesture, ICommand command)
        {
            CommandManager.RegisterClassInputBinding(type,
                    new InputBinding(command, gesture));
           
        }

        /// <summary>
        /// 注册局部热键（当所在控件的根（window或者其他）具有焦点时，响应热键）
        /// </summary>
        /// <param name="type">获得焦点的元素的类型</param>
        /// <param name="hotkeystring">快捷键字符串，例如：CTRL+C</param>
        /// <param name="command">响应热键的命令</param>
        public static void RegisterHotKey(Type type, string hotkeystring, ICommand command)
        {
            CommandManager.RegisterClassInputBinding(type,
                    new InputBinding(command, (KeyGesture)new KeyGestureConverter().ConvertFromString(hotkeystring)));
        } 
        #endregion

        #region 注销热键

        /// <summary>
        /// 撤销指定的热键
        /// </summary>
        /// <param name="wnd">热键所在Window</param>
        /// <param name="id">热键编号（注册时的编号）</param>
        /// <returns></returns>
        public static bool UnregisterHotKey(Window wnd, int id)
        {
            return UnregisterHotKey(GetHandle(wnd), id); ;
        }

        /// <summary>
        /// 撤销指定的热键
        /// </summary>
        /// <param name="wnd">热键所在window</param>
        /// <param name="controlKey">控制键</param>
        /// <param name="virtualKey">虚拟键</param>
        /// <returns></returns>
        public static bool UnregisterHotKey(Window wnd, ModifierKeys controlKey, System.Windows.Forms.Keys virtualKey)
        {
            KeyDic[wnd].Remove(BuildHotKeyId(controlKey, virtualKey));
            return UnregisterHotKey(GetHandle(wnd), BuildHotKeyId(controlKey, virtualKey)); ;
        }

        /// <summary>
        /// 撤销该Window所有热键
        /// </summary>
        /// <param name="wnd"></param>
        /// <returns></returns>
        public static bool UnregisterHotKey(Window wnd)
        {
            KeyDic.Remove(wnd);
            return true;
        }

        #endregion

        #endregion

        #region 私有方法

        /// <summary>
        /// 注册热键
        /// </summary>
        /// <param name="wnd"></param>
        /// <param name="controlKey"></param>
        /// <param name="virtualKey"></param>
        /// <param name="keyid"></param>
        /// <param name="commandOrMethod"></param>
        /// <returns></returns>
        private static bool DefaultRegistHotKey(Window wnd, ModifierKeys controlKey, System.Windows.Forms.Keys virtualKey, int keyid, object commandOrMethod)
        {
            if (KeyDic.ContainsKey(keyid))
            {
                System.Diagnostics.Debug.WriteLine("【热键管理器】：热键已经被注册!");
            }

            //注册热键
            if (false == RegisterHotKey(GetHandle(wnd), keyid, (uint)controlKey, (uint)virtualKey))
            {
                System.Diagnostics.Debug.WriteLine("【热键管理器】：热键注册失败!");
            }
            return InstallHotKeyHook(wnd, keyid, commandOrMethod);
        }

        /// <summary>
        /// 安装热键处理挂钩
        /// </summary>
        /// <param name="wnd"></param>
        /// <returns></returns>
        private static bool InstallHotKeyHook(Window wnd, int keyid, object commandOrMethod)
        {
            if (wnd == null || GetHandle(wnd) == IntPtr.Zero)
                return false;

            //获得消息源
            System.Windows.Interop.HwndSource source = System.Windows.Interop.HwndSource.FromHwnd(GetHandle(wnd));
            if (source == null) return false;

            //挂接事件
            if (!KeyDic.ContainsKey(wnd))
            {
                source.AddHook(HotKeyHook);
                KeyDic.Add(wnd, new Dictionary<int, object>());
            }
            KeyDic[wnd].Add(keyid, commandOrMethod);
            return true;
        }

        /// <summary>
        /// 热键处理程序
        /// </summary>
        /// <param name="hwnd">热键window所在指针</param>
        /// <param name="msg">键盘消息</param>
        /// <param name="wParam">键盘消息号</param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        private static IntPtr HotKeyHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            Window currentWnd = (Window)System.Windows.Interop.HwndSource.FromHwnd(hwnd).RootVisual;
            if (msg == WM_HOTKEY)
            {
                if (KeyDic[currentWnd].ContainsKey(wParam.ToInt32()))
                {
                    int index = wParam.ToInt32();
                    if (KeyDic[currentWnd][index].GetType() == typeof(KeyEventHandler) || KeyDic[currentWnd][index].GetType() == typeof(System.Windows.Forms.KeyEventHandler))
                    {
                        Delegate currentMethod = (Delegate)KeyDic[currentWnd][wParam.ToInt32()];
                        ParameterInfo[] palist = currentMethod.Method.GetParameters();
                        object[] newparlist = new object[palist.Length];
                        for (int i = 0; i < palist.Length; i++)
                        {
                            if (palist[i].ParameterType == typeof(System.Windows.Input.KeyEventArgs))
                            {
                                Keys keystr = (Keys)Enum.Parse(typeof(Keys), (wParam.ToInt32() / 10).ToString());
                                System.Windows.Input.KeyEventArgs keyEventArgs = new
                                      KeyEventArgs(InputManager.Current.PrimaryKeyboardDevice,
                                      InputManager.Current.PrimaryKeyboardDevice.ActiveSource,
                                      System.Environment.ProcessorCount, (Key)Enum.Parse(typeof(Key), keystr.ToString()));
                                newparlist[i] = keyEventArgs;

                            }
                            if (palist[i].ParameterType == typeof(System.Windows.Forms.KeyEventArgs))
                            {
                                Keys key = (Keys)Enum.Parse(typeof(Keys), (wParam.ToInt32() / 10).ToString());
                                ModifierKeys modifierkeys = (ModifierKeys)Enum.Parse(typeof(ModifierKeys), (wParam.ToInt32() % 10).ToString());
                                System.Windows.Forms.KeyEventArgs keyEventArgs =
                                    new System.Windows.Forms.KeyEventArgs(key | (Keys)Enum.Parse(typeof(Keys), modifierkeys.ToString()));
                                newparlist[i] = keyEventArgs;
                            }
                        }
                        ((Delegate)KeyDic[currentWnd][wParam.ToInt32()]).DynamicInvoke(newparlist);
                    }
                    if (KeyDic[currentWnd][index] is ICommand)
                    {
                        ICommand cmd = (ICommand)KeyDic[currentWnd][index];
                        if (cmd.CanExecute(null))
                        {
                            cmd.Execute(null);
                        }
                    }
                }
            }
            return IntPtr.Zero;
        }

        /// <summary>
        /// 获取WPF window的指针
        /// </summary>
        /// <param name="wnd"></param>
        /// <returns></returns>
        private static IntPtr GetHandle(Window wnd)
        {
            return new WindowInteropHelper(wnd).Handle;
        }

        /// <summary>
        /// 生成热键编号
        /// </summary>
        /// <param name="controlKey">控制键</param>
        /// <param name="virtualKey">虚拟键</param>
        /// <returns></returns>
        private static int BuildHotKeyId(ModifierKeys controlKey, System.Windows.Forms.Keys virtualKey)
        {
            return (int)controlKey + (int)virtualKey * 10;
        } 

        #endregion

    }
}
